import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { DataSource, Repository } from "typeorm";
import { TombPitEntity } from "../entities/tomb-pit.entity";
import { TombDailyCounterEntity } from "../entities/tomb-daily-counter.entity";
import { TombDigLogEntity } from "../entities/tomb-dig-log.entity";
import { CharacterResourcesEntity } from "../entities/character-resources.entity";
import { GemService } from "./gem.service";
import { InventoryService } from "./inventory.service";

export interface PitStateItem {
  state: number; // 0,1..9
  pitId: number; // tomb_pit.id
}

export interface QueryPitsResult {
  pits: PitStateItem[];
  limit: { used: number; cap: number; remain: number };
}

export interface BuyShovelResult {
  status: 1;
  timesAfter: number;
  flag: number;
}

export interface DigResult {
  pageIdxOrCode: number; // 0
  resultType: number; // 2 奖励；3 空；9 翻页
  timesLeft: number; // remainShovel after consume
  rewardType: number; // 1..8 或 9
  rewardDesc: string; // 文案
  message: string; // msg
}

@Injectable()
export class TombService {
  private static readonly DAILY_CAP = 300;

  constructor(
    @InjectRepository(TombPitEntity)
    private readonly pitRepo: Repository<TombPitEntity>,
    @InjectRepository(TombDailyCounterEntity)
    private readonly dailyRepo: Repository<TombDailyCounterEntity>,
    @InjectRepository(TombDigLogEntity)
    private readonly logRepo: Repository<TombDigLogEntity>,
    private readonly dataSource: DataSource,
    private readonly gemService: GemService,
    private readonly inventoryService: InventoryService
  ) {}

  async queryPits(
    characterId: number,
    dateStr: string
  ): Promise<QueryPitsResult> {
    const daily = await this.getOrCreateDaily(characterId, dateStr);
    const pageIndex = await this.getCurrentPageIndex(characterId);
    const pits = await this.getOrCreatePage(characterId, pageIndex);
    const list: PitStateItem[] = pits
      .sort((a, b) => a.pitIndex - b.pitIndex)
      .map((p) => ({ state: p.state, pitId: p.id }));
    return {
      pits: list,
      limit: {
        used: daily.usedTimes,
        cap: TombService.DAILY_CAP,
        remain: Math.max(0, TombService.DAILY_CAP - daily.usedTimes),
      },
    };
  }

  async buyShovel(
    characterId: number,
    dateStr: string,
    count: number
  ): Promise<BuyShovelResult> {
    const inc = count > 0 ? 100 : 0;
    const daily = await this.getOrCreateDaily(characterId, dateStr);
    daily.remainShovel += inc;
    await this.dailyRepo.save(daily);
    return {
      status: 1,
      timesAfter: daily.remainShovel,
      flag: count > 0 ? 1 : 0,
    };
  }

  async getRemainShovel(characterId: number, dateStr: string): Promise<number> {
    const daily = await this.getOrCreateDaily(characterId, dateStr);
    return daily.remainShovel;
  }

  async dig(
    characterId: number,
    pitId: number,
    dateStr: string
  ): Promise<DigResult> {
    return await this.dataSource.transaction(async (manager) => {
      const dailyRepo = manager.getRepository(TombDailyCounterEntity);
      const pitRepo = manager.getRepository(TombPitEntity);
      const logRepo = manager.getRepository(TombDigLogEntity);

      // 日计数
      let daily = await dailyRepo.findOne({ where: { characterId, dateStr } });
      if (!daily) {
        daily = dailyRepo.create({
          characterId,
          dateStr,
          usedTimes: 0,
          remainShovel: 0,
          created_at: new Date(),
          updated_at: null,
        });
        await dailyRepo.save(daily);
      }
      if (daily.usedTimes >= TombService.DAILY_CAP) {
        throw new Error("今日探穴次数已达上限");
      }
      if (daily.remainShovel <= 0) {
        throw new Error("您的探穴次数不足，请先购买洛阳铲");
      }

      const pit = await pitRepo.findOne({ where: { id: pitId, characterId } });
      if (!pit) {
        throw new Error("坑位不存在");
      }
      if (pit.state !== 0) {
        throw new Error("该坑位已挖过");
      }

      // 奖励
      const reward = this.randomReward();

      // 更新坑位
      pit.state = reward.t;
      await pitRepo.save(pit);

      // 递增计数
      daily.usedTimes += 1;
      daily.remainShovel -= 1;
      await dailyRepo.save(daily);

      // 发放奖励（金币/银币/宝石/物品）
      await this.applyReward(manager, characterId, reward);

      // 记录日志（挖空或银币不记录）
      const pageIndex = pit.pageIndex;
      const pitIndex = pit.pitIndex;
      const resultType = reward.t === 9 ? 3 : 2;
      if (reward.t !== 9 && reward.t !== 1) {
        const log = logRepo.create({
          characterId,
          pitId: pit.id,
          pageIndex,
          pitIndex,
          resultType,
          rewardType: reward.t,
          rewardDesc: reward.v,
          created_at: new Date(),
          updated_at: null,
        });
        await logRepo.save(log);
      }

      // 判断是否翻页（当前页9个全部不为0）
      const unfinished = await pitRepo.count({
        where: { characterId, pageIndex, state: 0 },
      });
      if (unfinished === 0) {
        // 创建下一页
        const nextIndex = pageIndex + 1;
        await this.ensurePage(manager, characterId, nextIndex);
        const pageIdxOrCode = reward.t !== 1 && reward.t !== 9 ? 1 : 0;
        return {
          pageIdxOrCode,
          resultType: 9,
          timesLeft: daily.remainShovel,
          rewardType: reward.t,
          rewardDesc: reward.v,
          message: reward.msg,
        };
      }

      const pageIdxOrCode = reward.t !== 1 && reward.t !== 9 ? 1 : 0;
      return {
        pageIdxOrCode,
        resultType: reward.t === 9 ? 3 : 2,
        timesLeft: daily.remainShovel,
        rewardType: reward.t,
        rewardDesc: reward.v,
        message: reward.msg,
      };
    });
  }

  async historyRecent(
    limit: number
  ): Promise<Array<{ id: number; n: string; t: string; v: string }>> {
    // 仅返回结构，名称由上层提供；t 返回为时间字符串 HH:mm
    const rows = await this.logRepo.find({
      order: { created_at: "DESC" },
      take: limit,
    });
    const pad = (n: number) => String(n).padStart(2, "0");
    return rows.map((r) => {
      const d = r.created_at ? new Date(r.created_at) : new Date();
      const hh = pad(d.getHours());
      const mm = pad(d.getMinutes());
      return {
        id: r.characterId,
        n: String(r.characterId),
        t: `${hh}:${mm}`,
        v: r.rewardDesc,
      };
    });
  }

  private async getOrCreateDaily(
    characterId: number,
    dateStr: string
  ): Promise<TombDailyCounterEntity> {
    let d = await this.dailyRepo.findOne({ where: { characterId, dateStr } });
    if (d) return d;
    d = this.dailyRepo.create({
      characterId,
      dateStr,
      usedTimes: 0,
      remainShovel: 0,
      created_at: new Date(),
      updated_at: null,
    });
    return await this.dailyRepo.save(d);
  }

  private async getCurrentPageIndex(characterId: number): Promise<number> {
    const max = await this.pitRepo
      .createQueryBuilder("p")
      .select("MAX(p.pageIndex)", "max")
      .where("p.characterId = :characterId", { characterId })
      .getRawOne<{ max: number | null }>();
    const pageIndex = typeof max?.max === "number" ? max.max : 0;
    // 确保该页存在
    await this.ensurePage(this.dataSource, characterId, pageIndex);
    // 如果该页全部已挖，则预创建下一页（不切换）
    const unfinished = await this.pitRepo.count({
      where: { characterId, pageIndex, state: 0 },
    });
    if (unfinished === 0) {
      await this.ensurePage(this.dataSource, characterId, pageIndex + 1);
    }
    return pageIndex;
  }

  private async getOrCreatePage(
    characterId: number,
    pageIndex: number
  ): Promise<TombPitEntity[]> {
    const rows = await this.pitRepo.find({ where: { characterId, pageIndex } });
    if (rows.length === 9) return rows;
    await this.ensurePage(this.dataSource, characterId, pageIndex);
    return await this.pitRepo.find({ where: { characterId, pageIndex } });
  }

  private async ensurePage(
    dsOrManager: DataSource | { getRepository: DataSource["getRepository"] },
    characterId: number,
    pageIndex: number
  ): Promise<void> {
    const repo = dsOrManager.getRepository(TombPitEntity);
    const count = await repo.count({ where: { characterId, pageIndex } });
    if (count === 9) return;
    const toInsert: TombPitEntity[] = [];
    for (let i = 0; i < 9; i++) {
      const pit = repo.create({
        characterId,
        pageIndex,
        pitIndex: i,
        state: 0,
        created_at: new Date(),
        updated_at: null,
      });
      toInsert.push(pit);
    }
    await repo.save(toInsert);
  }

  private randomReward(): {
    t: number;
    v: string;
    msg: string;
    goldAmount?: number;
    silverAmount?: number;
    gemLevel?: number;
    itemId?: number;
    itemCount?: number;
  } {
    // 概率：空30%，银币30%，金币15%，矿石10%，宝石7%，宝盒4%，鲜花2%，代金2%
    const table: Array<{ t: number; w: number }> = [
      { t: 9, w: 30 },
      { t: 1, w: 30 },
      { t: 8, w: 15 },
      { t: 3, w: 10 },
      { t: 4, w: 7 },
      { t: 5, w: 4 },
      { t: 6, w: 2 },
      { t: 7, w: 2 },
    ];
    const total = table.reduce((s, x) => s + x.w, 0);
    let r = Math.random() * total;
    let pick = 9;
    for (const item of table) {
      if (r < item.w) {
        pick = item.t;
        break;
      }
      r -= item.w;
    }
    if (pick === 9) return { t: 9, v: "空", msg: "挖空了" };
    if (pick === 8) {
      const amt = Math.random() < 0.5 ? 10 : 20;
      return {
        t: 8,
        v: `${amt} 个 金币`,
        msg: `恭喜您挖到 ${amt} 金币`,
        goldAmount: amt,
      };
    }
    if (pick === 1) {
      const amt = 100 + Math.floor(Math.random() * 901);
      return {
        t: 1,
        v: `${amt} 银币`,
        msg: `恭喜您挖到 ${amt} 银币`,
        silverAmount: amt,
      };
    }
    if (pick === 3) {
      const kinds = [
        { id: 38, name: "铁矿" },
        { id: 48, name: "银矿" },
        { id: 58, name: "金矿" },
      ];
      const lvl = 1 + Math.floor(Math.random() * 3); // 1-3级
      const kind = kinds[Math.floor(Math.random() * kinds.length)];
      const itemId = kind.id + (lvl - 1); // 38/39/40, 48/49/50, 58/59/60 对应1-3级
      const name = `${lvl}级${kind.name}`;
      return {
        t: 3,
        v: `1 块 ${name}`,
        msg: `恭喜您挖到 1 块 ${name}`,
        itemId,
        itemCount: 1,
      };
    }
    if (pick === 4) {
      const lvl = 1 + Math.floor(Math.random() * 3);
      return {
        t: 4,
        v: `1 个 ${lvl} 级紫水晶`,
        msg: `恭喜您挖到 1 个 ${lvl} 级紫水晶`,
        gemLevel: lvl,
      };
    }
    if (pick === 5) {
      const boxPool = [
        { id: 154, name: "原矿宝盒" },
        { id: 155, name: "精矿宝盒" },
        { id: 156, name: "珍矿宝盒" },
        { id: 432, name: "珍矿宝盒" },
      ];
      const picked = boxPool[Math.floor(Math.random() * boxPool.length)];
      return {
        t: 5,
        v: `1 个 ${picked.name}`,
        msg: `恭喜您挖到 1 个 ${picked.name}`,
        itemId: picked.id,
        itemCount: 1,
      };
    }
    if (pick === 6)
      return {
        t: 6,
        v: `1 朵 鲜花`,
        msg: `恭喜您挖到 1 朵 鲜花`,
        itemId: 9,
        itemCount: 1,
      };
    if (pick === 7) {
      return {
        t: 7,
        v: `1 个 代金`,
        msg: `恭喜您挖到 1 个 代金`,
        itemId: 1000,
        itemCount: 1,
      };
    }
    return { t: 9, v: "空", msg: "挖空了" };
  }

  private async applyReward(
    manager: any,
    characterId: number,
    reward: {
      t: number;
      goldAmount?: number;
      silverAmount?: number;
      gemLevel?: number;
      itemId?: number;
      itemCount?: number;
    }
  ): Promise<void> {
    if (reward.t === 8 && reward.goldAmount) {
      const repo = manager.getRepository(CharacterResourcesEntity);
      const row = await repo.findOne({ where: { characterId } });
      if (row) {
        await repo.update(
          { id: row.id },
          { gold: (row.gold || 0) + reward.goldAmount, updated_at: new Date() }
        );
      }
      return;
    }
    if (reward.t === 1 && reward.silverAmount) {
      const repo = manager.getRepository(CharacterResourcesEntity);
      const row = await repo.findOne({ where: { characterId } });
      if (row) {
        await repo.update(
          { id: row.id },
          {
            silver: (row.silver || 0) + reward.silverAmount,
            updated_at: new Date(),
          }
        );
      }
      return;
    }
    if (reward.t === 4 && reward.gemLevel) {
      // 使用与2032一致的属性风格名称，避免出现“等级X”命名
      await this.gemService.awardRandomAttributeGem(
        characterId,
        reward.gemLevel
      );
      return;
    }
    if (reward.itemId && reward.itemCount) {
      // 奖励为矿石/矿盒（t=3矿石、5宝盒）发到矿石背包；其余走杂物背包
      if (reward.t === 3 || reward.t === 5) {
        try {
          await this.inventoryService.addMineral(
            characterId,
            reward.itemId,
            reward.itemCount
          );
        } catch (_) {}
        return;
      }
      if (reward.t === 6 || reward.t === 7) {
        try {
          await this.inventoryService.addItemToInventory(
            characterId,
            reward.itemId,
            reward.itemCount
          );
        } catch (_) {}
        return;
      }
    }
  }
}
